<?php
/*

  Copyright (c) 2008   by  maxim.oransky <maxim.oransky@gmail.com>,
  auhtor of  the-wheel-library  team

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

class LjClient {

    const HOST_NAME = 'www.livejournal.com';
    const PORT = 80;
    const TIMEOUT_SECONDS = 20;
    const MAX_ATTEMPTS = 5;

    private $user = '';
    private $pass = '';

    private $arrCookies = array();
//    private $arrAccess = array();
    private $name = '';

    /**
     * @return LjPost
     */
    public static function createPost() { return new LjPost(); }

    public function __construct($user, $pass, $isDebugMode = false) {

        $this->user = $user;
        $this->pass = $pass;
        $this->isDebugMode = $isDebugMode;

        $this->login();

        $this->log('Welcome, ' . $this->name);
    }

    public function getFriends() {

        $hParams = $this->getLoginParams();
        $hParams['mode'] = 'getfriends';
        $hResponse = $this->askServer($hParams);
        $this->log('Friends: ' . print_r($hResponse, 1));
    }

    public function deleteFriend($username) {

        $hParams = $this->getLoginParams();
        $this->log("Delete $username");

        $anyUniqueInteger = mt_rand(1,25000);
        $hParams['mode'] = 'editfriends';
        $hParams['editfriend_delete_' . $username] = 1;

        $hResponse = $this->askServer($hParams);

        $this->log('Done: ' . print_r($hResponse, 1));
    }

    public function addFriend($username) {

        $hParams = $this->getLoginParams();

        $this->log("Adding $username");

        $anyUniqueInteger = mt_rand(1,25000);
        $hParams['mode'] = 'editfriends';
        $hParams['editfriend_add_' . $anyUniqueInteger . '_user'] = $username;

        $hResponse = $this->askServer($hParams);

        $this->log('Done: ' . print_r($hResponse, 1));
    }

    public function post(LjPost $oPost) {

        $this->log('Posting... ');

        $oPost->checkIsCorrect();

        $hParams = $this->getLoginParams();

        $hParams['mode'] = 'postevent';

        $hParams['event'] = $oPost->getText();
        $hParams['lineendings'] = $oPost->getLineEndings();
        $hParams['subject'] = $oPost->getSubj();

        if ($security = $oPost->getSecurity()) {
            $hParams['security'] = $security;
            if ($allowmask = $oPost->getAllowmask()) {
                $hParams['allowmask'] = $allowmask;
            }
        }

        $hParams['year'] = $oPost->getYear();
        $hParams['mon']  = $oPost->getMon();
        $hParams['day']  = $oPost->getDay();
        $hParams['hour'] = $oPost->getHour();
        $hParams['min']  = $oPost->getMin();
        //'propname' =>

        $hResponse = $this->askServer($hParams);

        $this->log('Done: ' . $hResponse['url']);
    }

    private function login() {

        $this->log('Login as ' . $this->user);

        $hParams = $this->getLoginParams();
        $hParams['mode'] = 'login';

        $hResponse = $this->askServer($hParams);

//        unset($hResponse['access']['count']);
//        $this->arrAccess = array_values($hResponse['access']);

        $this->name = $hResponse['name'];
    }

    private function getLoginParams() {
        return array(
            'user' => $this->user,
            'auth_method' => 'clear', // deprecated. TODO make better
            'hpassword' => md5($this->pass),
            'ver' => 1,
        );
    }

    private function checkResponseOk(array $hResponse) {
        if ($hResponse['success'] != 'OK')
            throw new Exception('Failed:' . print_r($hResponse, 1));
    }

    private $isDebugMode = false;

    private function log($str) {
        if ($this->isDebugMode) {
            echo $str . "\n";
        }
    }

    private function convetFlatResponseToHash(array $arrResponse) {

        $hResponse = array();

        $numArrResponse = count($arrResponse);
        if ($numArrResponse % 2) $numArrResponse--;

        for ($i = 0; $i < $numArrResponse; $i += 2) {

            $key = $arrResponse[$i];
            $val = $arrResponse[$i + 1];

            $arrKeyParts = explode('_', $key);
            //$this->log(print_r($arrKeyParts,1));

            switch (count($arrKeyParts)) {

                case 1:
                    $hResponse[ $arrKeyParts[0] ] = $val;
                    break;

                case 2:

                    if (!isset($hResponse[ $arrKeyParts[0] ]))
                        $hResponse[ $arrKeyParts[0] ] = array();

                    $hResponse[ $arrKeyParts[0] ][ $arrKeyParts[1] ] = $val;

                    break;

                case 3:

                    if (!isset($hResponse[ $arrKeyParts[0] ]))
                        $hResponse[ $arrKeyParts[0] ] = array();

                    if (!isset($hResponse[ $arrKeyParts[0] ][ $arrKeyParts[1] ]))
                        $hResponse[ $arrKeyParts[0] ][ $arrKeyParts[1] ] = array();

                    $hResponse[ $arrKeyParts[0] ][ $arrKeyParts[1] ][ $arrKeyParts[2] ] = $val;

                    break;

                default:
                    throw new Exception("Incorrect response key: $key");
            }
        }

        return $hResponse;
    }

    private function askServer(array $hFields, $isDebug = false) {

        $connect = new SocketConnect(self::HOST_NAME, self::PORT, self::TIMEOUT_SECONDS);

        $content = SocketConnect::composeData($hFields);

        if ($isDebug) {
            echo $content;
            return array();
        }

        $connect->addLine('POST /interface/flat HTTP/1.0');
        $connect->addLine('Host: ' . self::HOST_NAME);
        $connect->addLine('Content-type: application/x-www-form-urlencoded');
        $connect->addLine('Content-length: ' . strlen($content));
        $connect->addSeparator();
        $connect->addLine($content);

        $response = $connect->send();

        $this->arrCookies = $response->getCookies();

        $hResponse = $this->convetFlatResponseToHash($response->getContentAsLinesArray());

        if (empty($hResponse))
            throw new Exception('Empty response');

        $this->checkResponseOk($hResponse);

        return $hResponse;
    }
}

class LjPost {

    private $subj = '';
    public function setSubj($str) { $this->subj = strtr($str, array("\r" => '', "\n" => '')); }
    public function getSubj() { return $this->subj; }

    private $text = '';
    public function setText($str) { $this->text = strtr($str, array("\r" => '')); }
    public function getText() { return $this->text; }

    private $time = 0;
    public function setDate(Date $oDate = null) { $this->time = $oDate ? $oDate->getTimestamp() : time(); }
    //public function getTime() { return $this->time; }
    public function getYear() { return date('Y', $this->time); }
    public function getMon() { return date('n', $this->time); }
    public function getDay() { return date('j', $this->time); }
    public function getHour() { return date('G', $this->time); }
    public function getMin() { return date('i', $this->time); }

    private $security = '';
    private $allowmask = 0;
    public function setSecurityPublic() { $this->security = 'public'; }
    public function setSecurityPrivate() { $this->security = 'private'; }
    public function setSecurityMask($mask) {
        $this->security = 'usemask';
        $this->allowmask = $mask;
    }
    public function getSecurity() { return $this->security; }
    public function getAllowmask() { return $this->allowmask; }


    public function getLineEndings() { return 'unix'; } // unix, pc, mac

    public function checkIsCorrect() {

        if (!$this->getText())
          throw new Exception('Text cannot be emty!');

        return true;
    }
}

?>
